package storage

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"strings"

	"github.com/go-logr/logr"
	"github.com/go-logr/stdr"
	"go.beyondstorage.io/v5/pairs"
	"go.beyondstorage.io/v5/services"
	"go.beyondstorage.io/v5/types"

	stdlog "log"
)

const (
	file_keep = ".filekeep"
	file_keep_Data = "this is keep file"
)

type FileSys struct {
	//日志记录器
	logger logr.Logger
	//链接串
	ConnStr string
	//存储类
	store   types.Storager
	//地址访问前缀
	AccessPrefix string
}





// CreateFileSys ， 创建文件操作类
// 参数：
//      connStr ： 存储链接串
//      logger ： 日志类
// 返回值：
//      FileSys：文件操作类指针
//      error: 异常
func CreateFileSys(connStr string,logger ...*logr.Logger) (*FileSys, error) {
	var sys_loger *logr.Logger
	if(len(logger) > 0 && logger[0] != nil){
		sys_loger = logger[0]
	}else{

		stdr.SetVerbosity(1)
		deflogger := stdr.NewWithOptions(stdlog.New(os.Stderr, "", stdlog.LstdFlags), stdr.Options{LogCaller: stdr.All})
		sys_loger = &deflogger
		
	}

	store, err := services.NewStoragerFromString(connStr)

	if (err != nil) {
		sys_loger.Error(err,"CreateFileSys error","connStr",connStr)
		return nil,err
	}


	file_sys := &FileSys{
		logger: *sys_loger,
		ConnStr: connStr,
		store: store,
	}

	return file_sys,nil
}

// BuildConnStr ， 构建存储链接串
// 参数：
//      storage_name ： 存储类型
//      bucket_name ：  存储桶名称
//      work_dir ：  工作目录,存在的话左侧加"/"
//      endpoint ：  端点
//      app_key ：  app_key
//      secret_key ：  secret_key
// 返回值：
//      存储链接串
func BuildConnStr(storage_name,bucket_name,work_dir,endpoint,app_key,secret_key string)string{
	connStr := fmt.Sprintf(
		"%s://%s%s?credential=%s&endpoint=%s",
		storage_name,
		bucket_name,
		work_dir,
		fmt.Sprintf("hmac:%s:%s", app_key, secret_key),
		endpoint,
	)

	return connStr

}


func IsDir(fileObject *types.Object)bool{

	result := false 

	if(fileObject.GetMode().IsDir()){ return true}


	if(fileObject.Path[len(fileObject.Path)-1:]=="/"){result = true}

	return result 

}


// CreateDir ， 创建目录
// 参数：
//      path ： 目录路径
// 返回值：
//      error: 异常
func (obj *FileSys) CreateDir(path string)(err error){
	if(obj.FileExist(path)){return nil}

	reader := strings.NewReader(file_keep_Data)


	_,err = obj.Write(path+"/"+file_keep,reader,reader.Size())
	return err
}

// Write ， 写入文件
// 参数：
//      path ： 路径
//      r ： 读取器
//      r ： 读取长度
// 返回值：
//      int64: 实际写入长度
//      error: 异常
func (obj *FileSys) Write(path string,r io.Reader , size int64, pairs ...types.Pair)(int64,error)  {
	wrsz,err := obj.store.Write(path,r,size,pairs...)
	if(err != nil){
		obj.logger.Error(err,"write file error","path",path)
	}
	return wrsz,err
}

func (obj *FileSys) Read(path string, w io.Writer, pairs ...types.Pair)(n int64, err error){
	n,err = obj.store.Read(path,w,pairs...)
	if(err != nil){
		obj.logger.Error(err,"read file error","path",path)
	}
	return n,err
}


// Copy ， 复制文件
// 参数：
//      srcPath ： 源文件路径
//      desPath ： 目标文件路径
// 返回值：
//      error: 异常
func (obj *FileSys) Copy(srcPath,desPath string)(err error){

	obj.logger.Info("copy file","srcPath",srcPath,"desPath",desPath)

	copier, ok := obj.store.(types.Copier)
	if ok {
		err = copier.Copy(srcPath,desPath)
		if(err != nil){
			obj.logger.Error(err,"copy file error","srcPath",srcPath,"desPath",desPath)
		}
		return err
	} else {//不支持复制接口
		obj.logger.Info("not supper Copier")

		buf := new(bytes.Buffer)
		size,err := obj.Read(srcPath,buf)
		if(err != nil){
			obj.logger.Error(err,"copy file error","srcPath",srcPath,"desPath",desPath)
			return err
		}
		
		_,err = obj.Write(desPath,buf,size)
		if(err != nil){
			obj.logger.Error(err,"copy file error","srcPath",srcPath,"desPath",desPath)
			return err
		}
		return nil 
	}
}


// Delete ， 删除文件
// 参数：
//      path ： 文件路径
// 返回值：
//      error: 异常
func (obj *FileSys) Delete(path string)(err error){

	err = obj.store.Delete(path)
	if(err != nil){
		obj.logger.Error(err,"file_sys delete error","path",path)
	}

	return err
}

// ListAll ， 目录下所有文件对象,含子目录
// 参数：
//      path ： 目录路径
// 返回值：
//      oi: 文件对象
//      error: 异常
func (obj *FileSys) ListAll(path string)(oi *types.ObjectIterator, err error){

	it, err := obj.store.List(path, pairs.WithListMode(types.ListModeDir))

	if(err != nil){
		obj.logger.Error(err,"file_sys list error","path",path)
	}
	return it,err

}


func (obj *FileSys) DirAllFileName(path string)(names []string, err error){

	it,err := obj.ListAll(path)
	
	if(err != nil){
		obj.logger.Error(err,"DirAllFileName err","path",path)
		return
	}
	
	for {
		o, err := it.Next()
		if(err != nil){
			obj.logger.Error(err,"DirAllFileName err","obj",o)
			break
		}
		names = append(names, o.Path)
	}

	return
}

// FileExist ， 判断文件是否存在
// 参数：
//      path ： 文件路径
// 返回值：
//      exist: true 存在,不存在
func (obj *FileSys) FileExist(path string)(exist bool){
	_, err := obj.store.Stat(path)
	return !errors.Is(err, services.ErrObjectNotExist)
}

// GetFileInfo ， 获取文件信息
// 参数：
//      path ： 文件路径
// 返回值：
//      res: 文件信息
func (obj *FileSys) GetFileInfo(path string)(res *types.Object){
	res, err := obj.store.Stat(path)
	if (err != nil){
		obj.logger.Error(err,"GetFileInfo err","path",path)
		return nil
	}
	return res

	
}

// CopyDir ， 复制目录
// 参数：
//      srcDir ： 源目录
//      dstPath： 目标目录
// 返回值：
//      error: 异常
func (obj *FileSys) CopyDir(srcDir,dstPath string)(err error){
	oblist,err := obj.ListAll(srcDir)
	if err != nil {
		obj.logger.Error(err,"CopyTo error","srcDir",srcDir,"dstPath",dstPath)
		return err
	}

	for {
		var relativePath string
		o, err := oblist.Next()
		if err != nil && !errors.Is(err, types.IterateDone) {
			return err
		}
		if err != nil {
			obj.logger.Error(err,"CopyTo error","srcDir",srcDir,"dstPath",dstPath)
			break
		}

		obj.logger.Info("list ","fileobj",o.Path)		
		if IsDir(o) {// 百度OSS判断有问题
			dirpath := o.Path
			relativePath, err = obj.Relative(srcDir, dirpath)
			if err != nil {
				obj.logger.Error(err,"CopyTo error","srcDir",srcDir,"dstPath",dstPath)
				continue
			}

			if(relativePath == ".") {continue}

			err = obj.CopyDir(dirpath, dstPath+relativePath+"/")
			if err != nil {
				obj.logger.Error(err,"CopyTo error","srcDir",srcDir,"dstPath",dstPath)
				continue
			}
		}


		filePath := o.Path
		//相对路径计算
		relativePath, err = obj.Relative(srcDir, filePath)
		if err != nil {
			obj.logger.Error(err,"CopyTo error","srcDir",srcDir,"dstPath",dstPath)
			continue
		}
		
		err = obj.Copy(filePath,dstPath+relativePath)
		if err != nil {
			obj.logger.Error(err,"CopyTo error","srcDir",srcDir,"dstPath",dstPath)
			continue
		}
	}
	return nil
}



func (obj *FileSys) Relative(basepath, targpath string) (string, error) {

	if(len(basepath)>0 && basepath[0] != '/') {
		basepath = "/"+basepath
	}

	if(len(targpath)>0 && targpath[0] != '/') {
		targpath = "/"+targpath
	}

	relativePath, err := filepath.Rel(basepath, targpath)


	if err != nil {
		return "", err
	}
	return relativePath, nil
}

